package com.fox.stock.service.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import com.fox.stock.constant.StockConstant;
import com.fox.stock.mapper.*;
import com.fox.stock.pojo.domain.SysUserInfoDomain;
import com.fox.stock.pojo.entity.*;
import com.fox.stock.pojo.vo.req.LoginReqVo;
import com.fox.stock.pojo.vo.req.UserSearchByConditionReqVo;
import com.fox.stock.pojo.vo.resq.PageResult;
import com.fox.stock.service.UserService;
import com.fox.stock.utils.IdWorker;
import com.fox.stock.pojo.vo.resq.LoginRespVo;
import com.fox.stock.pojo.domain.PermissionDomain;
import com.fox.stock.pojo.vo.resq.R;
import com.fox.stock.pojo.vo.resq.ResponseCode;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.awt.*;

import java.util.*;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service("userService")
public class UserServiceImpl implements UserService {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private IdWorker idWorker;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private SysRoleMapper sysRoleMapper;

    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;

    @Autowired
    private SysPermissionMapper sysPermissionMapper;

    @Autowired
    private SysRolePermissionMapper sysRolePermissionMapper;

    @Override
    public SysUser selectByUsernameToSysUser(String username) {
        return sysUserMapper.selectByUsernameSysUser(username);
    }

    @Override
    public R<LoginRespVo> login(LoginReqVo loginReqVo) {
        // 1. 判断数据是否合法
        if (loginReqVo == null || StringUtils.isBlank(loginReqVo.getUsername())
                || StringUtils.isBlank(loginReqVo.getPassword())) {
            return R.error(ResponseCode.DATA_ERROR);
        }
        // 判断验证码和sessionId是否合法
        if (StringUtils.isBlank(loginReqVo.getCode()) || StringUtils.isBlank(loginReqVo.getSessionId())) {
            return R.error(ResponseCode.CHECK_CODE_NOT_EMPTY);
        }

        // 判断验证码是否正确
        String code = (String) redisTemplate.opsForValue().get(loginReqVo.getSessionId());
        if (StringUtils.isBlank(code) || !code.equalsIgnoreCase(loginReqVo.getCode())) {
            return R.error(ResponseCode.CHECK_CODE_ERROR);
        }

        // 数据合法 ,验证码正确,查询用户信息
        SysUser user = sysUserMapper.selectByUsernameSysUser(loginReqVo.getUsername());

        // 2. 判断用户是否存在, 用户名密码是否正确
        if (user == null || !passwordEncoder.matches(loginReqVo.getPassword(), user.getPassword())) {
            return R.error(ResponseCode.USERNAME_OR_PASSWORD_ERROR);
        }

        // 3. 正确,封装返回
        LoginRespVo respVo = new LoginRespVo();
        // BeanUtils.copyProperties(.,.) TODO: 必须属性名和类型一致,才可以封装进去,否则无法copy
        BeanUtils.copyProperties(user, respVo);

        // 4. 根据用户id查询当前用户拥有的权限集合

        List<SysPermission> list = sysUserMapper.getUserPermissions(respVo.getId());

        // 5. 递归用户的权限集合,组装处权限树(menus) 不包含按钮权限集合
        List<PermissionDomain> best = new ArrayList<>();
        getPermissionTree(list, best);

        // best集合就是目录集合(并且菜单已经链在目录下)
        // 6. 将best集合赋值给menus
        respVo.setMenus(best);

        // 7. 获取按钮权限标识集合
        List<String> perms = new ArrayList<>();
        for (SysPermission sysPermission : list) {
            if (sysPermission.getType() == 3) {
                perms.add(sysPermission.getCode());
            }
        }
        // 8. 将perms封装到返回数据中
        respVo.setPermissions(perms);

        return R.ok(respVo);
    }

    // 递归遍历获取组装权限树(menus)
    private void getPermissionTree(List<SysPermission> list, List<PermissionDomain> best) {
        for (SysPermission sysPermission : list) {
            if (sysPermission.getPid() == 0) {
                best.add(PermissionDomain.changeBySysPermission(sysPermission));
            }
        }
        for (SysPermission sysPermission : list) {
            if (sysPermission.getPid() != 0 && sysPermission.getType() == 2) {
                for (PermissionDomain permissionDomain : best) {
                    if (permissionDomain.getId() == sysPermission.getPid()) {
                        permissionDomain.getChildren().add(PermissionDomain.changeBySysPermission(sysPermission));
                    }
                }
            }
        }
//        for (SysPermission permission : list) {
//            if (permission.getPid() != 0 && permission.getType() == 3){
//                for (PermissionDomain permissionDomain : best) {
//                    List<PermissionDomain> children = permissionDomain.getChildren();
//                    for (PermissionDomain child : children) {
//                        if (child.getId() == permission.getPid()){
//                            child.getChildren().add(PermissionDomain.changeBySysPermission(permission));
//                        }
//                    }
//                }
//            }
//        }
    }

    private void getPermissionTree1(List<SysPermission> list, List<PermissionDomain> best) {
        for (SysPermission sysPermission : list) {
            if (sysPermission.getPid() == 0) {
                best.add(PermissionDomain.changeBySysPermission(sysPermission));
            }
        }
        for (SysPermission sysPermission : list) {
            if (sysPermission.getPid() != 0 && sysPermission.getType() == 2) {
                for (PermissionDomain permissionDomain : best) {
                    if (permissionDomain.getId() == sysPermission.getPid()) {
                        permissionDomain.getChildren().add(PermissionDomain.changeBySysPermission(sysPermission));
                    }
                }
            }
        }
        for (SysPermission permission : list) {
            if (permission.getPid() != 0 && permission.getType() == 3) {
                for (PermissionDomain permissionDomain : best) {
                    List<PermissionDomain> children = permissionDomain.getChildren();
                    for (PermissionDomain child : children) {
                        if (child.getId() == permission.getPid()) {
                            child.getChildren().add(PermissionDomain.changeBySysPermission(permission));
                        }
                    }
                }
            }
        }
    }

//    // 递归实现含有按钮的权限树
//    private List<SysPermission> getPermissionTree1(List<SysPermission> list){
//        for (SysPermission sysPermission : list) {
//            if (sysPermission.getType() != 1){
//                sysPermission.setch(getPermissionTree1())
//            }
//        }
//    }


    @Override
    public R<Map> getCaptchaCode() {
        // 生成校验码
        LineCaptcha captcha = CaptchaUtil.createLineCaptcha(250, 40, 4, 20);
        captcha.setBackground(Color.LIGHT_GRAY);

        // 获取图中校验码的数据
        String code = captcha.getCode();

        // 日志打印验证码
        log.info("验证码:{}", code);

        // 生成id
        String id = StockConstant.CHECK_PREFIX + String.valueOf(idWorker.nextId());

        // 获取图片的base64格式
        String image = captcha.getImageBase64();

        // 将id和正确验证码保存到redis中
        ValueOperations operations = redisTemplate.opsForValue();
        operations.set(id, code, 1, TimeUnit.MINUTES);

        // 组装响应结果
        HashMap<String, String> data = new HashMap<>();
        data.put("sessionId", id);
        data.put("imageData", image);

        return R.ok(data);

    }

    @Override
    public R<PageResult> findUserInfoByCondition(UserSearchByConditionReqVo condition) {
        // 配置分页插件
        PageHelper.startPage(condition.getPageNum(), condition.getPageSize());
        // 根据条件查询用户信息
        List<SysUser> sysUsers = sysUserMapper.getUserInfoByCondition(condition);
        // 获取查询的分页信息以及封装返回数据
        PageInfo<SysUser> pageInfo = new PageInfo<>(sysUsers);
        PageResult<SysUser> sysUserPageResult = new PageResult<>(pageInfo);
        return R.ok(sysUserPageResult);

    }

    @Override
    public R addUserInfo(SysUser sysUser) {
        // 一般前端不会传空数据

        // 前端给的只是基础数据, 我们自己还要封装id,加密password,deleted置为:1,创建时间和更新时间
        // 设置id
        sysUser.setId(idWorker.nextId());
        // 加密password
        sysUser.setPassword(passwordEncoder.encode(sysUser.getPassword()));
        sysUser.setDeleted(1);
        sysUser.setCreateTime(DateTime.now().toDate());
        sysUser.setUpdateTime(DateTime.now().toDate());

        // 将封装好的数据传入mapper层入库
        int count = sysUserMapper.insert(sysUser);

        if (count == 0) {
            return R.error("操作失败");
        }
        return R.ok("操作成功");

    }

    @Override
    public R<Map<String, Object>> getRolesInfo(String userId) {
        // 先获取所有的角色信息
        List<SysRole> sysRole = sysRoleMapper.findAll();

        // 获取用户具有的角色信息id
        List<String> list = sysUserRoleMapper.selectRoleIdByUserId(userId);

        // 封装数据
        Map<String, Object> map = new HashMap<>();
        map.put("allRole", sysRole);
        map.put("ownRoleIds", list);
        return R.ok(map);
    }

    @Override
    public R upDateUsereRolesInfo(Map<String, Object> date) {
        // 先获取用户id和要更新的用户id
        String userId = (String) date.get("userId");

        List<String> roleIds = (List<String>) date.get("roleIds");

        // 再将这些数据封装到role和user的联合表的对象集合中
        List<SysUserRole> list = new ArrayList<>();
        for (String roleId : roleIds) {
            list.add(new SysUserRole(idWorker.nextId(), Long.parseLong(userId), Long.parseLong(roleId), DateTime.now().toDate()));
        }

        // 先删除现用户的所有角色id
        sysUserRoleMapper.deleteByUserId(userId);

        // 调用mapper层批量入库
        int count = sysUserRoleMapper.insertBatchIgnore(list);

        if (count == 0) {
            return R.error("操作失败");
        }
        return R.ok("操作成功");

    }

    @Override
    public R deleteUserInfoByUserId(List<Long> list) {
        // 判断list是否为空
        if (list.isEmpty()) {
            return R.error(ResponseCode.DATA_ERROR);
        }

        // 删除用户之前得先把用户之前的权限用户表中的数据删除
        sysUserRoleMapper.deleteBatchByUserId(list);

        // 调用mapper层批量删除
        int count = sysUserMapper.deleteBatchByPrimaryKeys(list);

        if (count == 0) {
            return R.error(ResponseCode.ERROR);
        }

        return R.ok(ResponseCode.SUCCESS);
    }

    @Override
    public R<SysUser> findUserInfoById(String id) {
        // 调用mapper接口直接查询
        SysUser sysUser = sysUserMapper.selectByPrimaryKey(Long.parseLong(id));
        return R.ok(sysUser);
    }

    @Override
    public R upDateUserInfoById(SysUserInfoDomain sysUser) {
//         直接调用mapper层修改用户信息
        // 设置更新时间
        SysUser sysUser1 = new SysUser(sysUser);
        sysUser1.setUpdateTime(DateTime.now().toDate());
        int i = sysUserMapper.updateByPrimaryKeySelective(sysUser1); // 这里(SysUserInfoDomain)多此一举了 : 懒得改
        if (i == 0) {
            return R.error("操作失败");
        }
        return R.ok("操作成功");
    }

    @Override
    public R<PageResult<SysRole>> selectRoleInfoByPage(Map<String, Object> map) {
        // 取出分页信息
        Integer pageNum = (Integer) map.get("pageNum");
        Integer pageSize = (Integer) map.get("pageSize");

        // 封装分页
        PageHelper.startPage(pageNum, pageSize);

        // 查询当前所有的角色信息
        List<SysRole> all = sysRoleMapper.findAll();

        // 封装
        PageInfo<SysRole> pageInfo = new PageInfo<>(all);
        PageResult<SysRole> result = new PageResult<>(pageInfo);

        return R.ok(result);

    }

    @Override
    public R<List<PermissionDomain>> getAllPermissionTree() {
        // 获取所有权限集合
        List<SysPermission> list = sysPermissionMapper.getAllPermission();

        // 转化为权限树(不包含按钮)
        List<PermissionDomain> best = new ArrayList<>();
        getPermissionTree1(list, best);

        // 返回数据
        return R.ok(best);
    }

    @Override
    public R addRoleAndPermission(Map<String, Object> map) {
        // 获取角色名称
        String name = (String) map.get("name");
        // 获取描述信息
        String description = (String) map.get("description");
        // 角色id
        long id = idWorker.nextId();
        // 封装Role类设置id,status(默认1),delete(默认1) 并且增加角色信息
        int i = sysRoleMapper.insert(new SysRole(id, name, description, 1, DateTime.now().toDate(), DateTime.now().toDate(), 1));
        if (i == 0) {
            return R.error("操作失败");
        }

        // 增加角色相关的权限
        // 获取权限id
        List<String> permissionsIds = (List<String>) map.get("permissionsIds");
        // 根据角色id加入关联权限
        List<SysRolePermission> list = new ArrayList<>();
        for (String permissionsId : permissionsIds) {
            list.add(new SysRolePermission(idWorker.nextId(), id, Long.parseLong(permissionsId), DateTime.now().toDate()));
        }
        // 调用mapper层批量入库
        int count = sysRolePermissionMapper.insertBatch(list);
        if (count == 0) {
            return R.error("操作失败");
        }
        return R.ok("操作成功");
    }

    @Override
    public R<List<String>> getPermissionByroleId(String roleId) {
        // 直接根据角色id到mapper层中获取权限id
        List<String> list = sysRolePermissionMapper.getPermissionsByroleId(roleId);
        return R.ok(list);
    }

    @Override
    public R upDateRoleAndPermission(Map<String, Object> map) {
        // 更新角色信息
        // 从map获取角色信息并封装
        SysRole sysRole = new SysRole();
        sysRole.setId(Long.parseLong((String) map.get("id")));
        sysRole.setName((String) map.get("name"));
        sysRole.setDescription((String) map.get("description"));
        sysRole.setUpdateTime(DateTime.now().toDate());
        // 调用mapper层更新角色信息
        sysRoleMapper.updateByPrimaryKeySelective(sysRole);

        // 更新角色相关的权限信息
        // 先删除角色相关的旧权限信息
        sysRolePermissionMapper.deleteByRoleId(sysRole.getId());
        // 再添加角色相关的新权限信息
        List<SysRolePermission> list = new ArrayList<>();
        List<String> info = (List<String>) map.get("permissionsIds");
        for (String s : info) {
            list.add(new SysRolePermission(idWorker.nextId(), sysRole.getId(), Long.parseLong(s), DateTime.now().toDate()));
        }
        sysRolePermissionMapper.insertBatch(list);

        return R.ok("操作成功");
    }

    @Override
    public R deleteRoleAndPermissionByRoleId(String roleId) {
        // 一般前端不会传输错误id
        // 先调用mapper层删除角色
        int count = sysRoleMapper.deleteByPrimaryKey(Long.parseLong(roleId));
        if (count == 0) {
            return R.error("操作失败");
        }
        // 成功删除角色后再删除相关的权限
        sysRolePermissionMapper.deleteByRoleId(Long.parseLong(roleId));
        return R.ok("操作成功");
    }

    @Override
    public R updateRoleStatus(Long roleId, Integer status) {
        // 直接封装只有这两个信息的实体类,调用更新方法
        SysRole sysRole = new SysRole();
        sysRole.setId(roleId); // 设置id
        sysRole.setStatus(status); // 设置状态
//        sysRole.setUpdateTime(DateTime.now().toDate()); // 设置更新时间 (修改状态感觉应该不需要更新时间)
        // 调用mapper层更新
        int i = sysRoleMapper.updateByPrimaryKeySelective(sysRole);
        if (i == 0) {
            return R.error("操作失败");
        }
        return R.ok("操作成功");
    }

    @Override
    public R<List<SysPermission>> getAllPermissionInfo() {
        // 获取数据库中所有的权限集合
        List<SysPermission> allPermission = sysPermissionMapper.getAllPermission();
        // 判断数据是否非法
        if (allPermission.isEmpty()) {
            return R.error();
        }
        return R.ok(allPermission);
    }

    @Override
    public R<List<Map<String, Object>>> getAllPermissionTreeIgnore3() {
        // 获取权限树忽略按钮
        List<Map<String, Object>> list = sysPermissionMapper.getAllPermissionIgnore3();
        if (list.isEmpty()) {
            return R.error();
        }
        for (Map<String, Object> stringObjectMap : list) {
            stringObjectMap.put("id",stringObjectMap.get("id").toString());
        }
        return R.ok(list);
    }

    @Override
    public R addPermission(SysPermission sysPermission) {
        // 判断结构是否正确
        if (sysPermission.getPid() != 0 && sysPermission.getType() == 1) {
            return R.error("结构不合法");
        }
        if (sysPermission.getPid() != 0) {
            SysPermission permission = sysPermissionMapper.selectByPrimaryKey(sysPermission.getPid());
            if (permission.getType() != sysPermission.getType() - 1) {
                return R.error("结构不合法");
            }
        }
        // 设置创建时间和修改时间,是否删除和状态,设置id
        sysPermission.setId(idWorker.nextId());
        sysPermission.setStatus(1);
        sysPermission.setDeleted(1);
        sysPermission.setCreateTime(DateTime.now().toDate());
        sysPermission.setUpdateTime(DateTime.now().toDate());
        // 入库
        int count = sysPermissionMapper.insertSelective(sysPermission);
        if (count == 0) {
            return R.error("添加失败");
        }
        return R.ok("添加成功");

    }

    @Override
    public R deletePermissionById(Long permissionId) {
        // 先查看是否有子权限,如果有,则一起删除(根据permission表中,谁的pid等于传入的id)
        List<Long> list = sysPermissionMapper.selectSamePidAndId(permissionId);
        List<Long> temp = new ArrayList<>(list);
        for (Long s : list) {
            List<Long> longs = sysPermissionMapper.selectSamePidAndId(s);
            if (!longs.isEmpty()){
                temp.addAll(longs);
            }
        }
        temp.add(permissionId);
        // 根据集合中的权限id删除权限
        int i = sysPermissionMapper.deleteByPrimaryKeyBatch(temp);
        if (i == 0){
            return R.error("删除失败");
        }
        return R.ok("删除成功");
    }

    @Override
    public R upDatePermission(SysPermission sysPermission) {
        // 如果为空说明传入参数异常
        if (sysPermission.getId() == null){
            return R.error("添加失败");
        }
        int i = sysPermissionMapper.updateByPrimaryKeySelective(sysPermission);
        if (i == 0){
            return R.error("添加失败");
        }
        return R.ok("添加成功");
    }
}
